package com.atguigu.srb.core.service.impl;

import com.atguigu.srb.core.mapper.UserInfoMapper;
import com.atguigu.srb.core.pojo.entity.UserInfo;
import com.atguigu.srb.core.pojo.entity.UserLoginRecord;
import com.atguigu.srb.core.pojo.vo.UserInfoPageVO;
import com.atguigu.srb.core.pojo.vo.UserLoginVO;
import com.atguigu.srb.core.pojo.vo.UserRegisterVO;
import com.atguigu.srb.core.service.UserInfoService;
import com.atguigu.srb.core.service.UserLoginRecordService;
import com.atguigu.srb.result.ResponseEnum;
import com.atguigu.srb.util.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mysql.cj.util.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.UUID;

/**
 * <p>
 * 用户基本信息 服务实现类
 * </p>
 *
 * @author Atguigu
 * @since 2024-04-15
 */
@Service
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private UserLoginRecordService userLoginRecordService;

    @Override
    public void register(UserRegisterVO userRegisterVO) {
        String smsCodePrefix = "sms:code:1:";
        // 1. 校验参数
        Integer userType = userRegisterVO.getUserType();
        String mobile = userRegisterVO.getMobile();
        String password = userRegisterVO.getPassword();
        String code = userRegisterVO.getCode();
        // 1.1 非空校验
        SrbAssert.assertTrue(userType != null && !StringUtils.isNullOrEmpty(mobile) && !StringUtils.isNullOrEmpty(password) &&
                !StringUtils.isNullOrEmpty(code), ResponseEnum.REGISTER_NOT_NULL_ERROR);
        // 1.2 手机号格式校验
        SrbAssert.assertTrue(RegexValidateUtils.checkCellphone(mobile), ResponseEnum.MOBILE_ERROR);
        // 2. 校验是否已经注册
        UserInfo userInfo = this.getOne(Wrappers.lambdaQuery(UserInfo.class).eq(UserInfo::getMobile, mobile));
        SrbAssert.assertNull(userInfo, ResponseEnum.MOBILE_EXIST_ERROR);
        // 3. 校验验证码
        String codeFromRedis = stringRedisTemplate.opsForValue().get(smsCodePrefix + mobile);
        SrbAssert.assertNotNull(codeFromRedis, ResponseEnum.CODE_EXPIRE_ERROR);
        SrbAssert.assertTrue(code.equals(codeFromRedis), ResponseEnum.CODE_ERROR);
        // 4. 密码加密 --> 加密 加盐(6位随机数) 当前项目采用 MD5(MD5.encrypt(password) + salt) -> 防止暴力破解
        String salt = RandomUtils.getSixBitRandom();
        userRegisterVO.setPassword(MD5.encrypt(MD5.encrypt(password) + salt));
        // 5. 保存到数据库  -> 需要给一些属性设置默认值
        userInfo = new UserInfo();
        BeanUtils.copyProperties(userRegisterVO, userInfo);
        userInfo.setHeadImg("https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif");
        userInfo.setNickName("小刘_" + UUID.randomUUID().toString().replace("-", "").substring(0, 10));
        userInfo.setIntegral(0);
        userInfo.setBindStatus(0);
        userInfo.setBorrowAuthStatus(0);
        userInfo.setSalt(salt);
        this.save(userInfo);
        // 6. 注册成功后，清空缓存中的验证码
        stringRedisTemplate.delete(smsCodePrefix + mobile);
    }

    @Override
    public String login(UserLoginVO userLoginVO, HttpServletRequest request) {
        // 0. 非空校验
        String mobile = userLoginVO.getMobile();
        String password = userLoginVO.getPassword();
        Integer userType = userLoginVO.getUserType();
        SrbAssert.assertTrue(!StringUtils.isNullOrEmpty(mobile) && !StringUtils.isNullOrEmpty(password)
                && userType != null, ResponseEnum.LOGIN_INFO_MISS_ERROR);
        // 1. 校验用户是否存在
        UserInfo userInfo = this.getOne(Wrappers.lambdaQuery(UserInfo.class).eq(UserInfo::getMobile, mobile)
                .eq(UserInfo::getUserType, userType));
        SrbAssert.assertNotNull(userInfo, ResponseEnum.LOGIN_MOBILE_ERROR);
        // 2. 校验密码加密后是否匹配
        String encryptPwd = MD5.encrypt(MD5.encrypt(password) + userInfo.getSalt());
        SrbAssert.assertTrue(encryptPwd.equals(userInfo.getPassword()), ResponseEnum.LOGIN_PASSWORD_ERROR);
        // 3. 响应token
        String token = JwtUtils.createToken(userInfo.getId(), userInfo.getName());
        saveLoginLogs(request, userInfo, 0);
        return token;
    }

    @Override
    public Page<UserInfo> selectPage(UserInfoPageVO userInfoPageVO, Page<UserInfo> userInfoPage) {
        String mobile = userInfoPageVO.getMobile();
        Integer userType = userInfoPageVO.getUserType();
        Integer status = userInfoPageVO.getStatus();
        LambdaQueryWrapper<UserInfo> wrappers = Wrappers.lambdaQuery(UserInfo.class);
        if (!StringUtils.isNullOrEmpty(mobile)) {
            wrappers.like(UserInfo::getMobile, mobile);
        }
        if (userType != null) {
            wrappers.eq(UserInfo::getUserType, userType);
        }
        if (status != null) {
            wrappers.eq(UserInfo::getStatus, status);
        }
        this.page(userInfoPage, wrappers);
        return userInfoPage;
    }

    @Override
    public Boolean isBindMobile(String token) {
        Long userId = JwtUtils.getUserId(token);
        UserInfo userInfo = this.getOne(Wrappers.lambdaQuery(UserInfo.class).eq(UserInfo::getId, userId));
        return !StringUtils.isNullOrEmpty(userInfo.getMobile());
    }

    @Override
    public void updateMobileAndType(String token, UserLoginVO userLoginVO) {
        Long userId = JwtUtils.getUserId(token);
        String mobile = userLoginVO.getMobile();
        Integer userType = userLoginVO.getUserType();
        SrbAssert.assertTrue(RegexValidateUtils.checkCellphone(mobile), ResponseEnum.MOBILE_ERROR);
        SrbAssert.assertNotTrue(this.isMobileUsed(mobile), ResponseEnum.MOBILE_EXIST_ERROR);
        UserInfo userInfo = new UserInfo();
        userInfo.setId(userId);
        userInfo.setMobile(mobile);
        userInfo.setUserType(userType);
        this.updateById(userInfo);
    }

    @Override
    public Boolean isMobileUsed(String mobile) {
        int count = this.count(Wrappers.lambdaQuery(UserInfo.class).eq(UserInfo::getMobile, mobile));
        return count > 0;
    }

    @Override
    public UserInfo checkUserBaseInfo(String  token) {
        // 1. 校验用户的基本信息
        //    1.1 用户是否登陆
        //    1.2 用户的账号是否正常(未被冻结)
        Long userId = JwtUtils.getUserId(token);
        UserInfo userInfo = this.getById(userId);
        Integer status = userInfo.getStatus();
        SrbAssert.assertTrue(status == 1, ResponseEnum.LOGIN_LOKED_ERROR);
        //    1.3 用户是否绑定了汇付宝
        Integer bindStatus = userInfo.getBindStatus();
        SrbAssert.assertTrue(bindStatus == 1, ResponseEnum.USER_NO_BIND_ERROR);
        //    1.4 校验管理员审核的借款资格是否通过
        if (userInfo.getUserType() == 2) {
            Integer borrowAuthStatus = userInfo.getBorrowAuthStatus();
            SrbAssert.assertTrue(borrowAuthStatus == 2, ResponseEnum.USER_NO_AMOUNT_ERROR);
        }
        return userInfo;
    }

    @Override
    public UserInfo getUserInfoByToken(String token) {
        Long userId = JwtUtils.getUserId(token);
        UserInfo userInfo = this.getById(userId);
        return userInfo;
    }

    public void saveLoginLogs(HttpServletRequest request, UserInfo userInfo, Integer loginType) {
        // 4. 保存登陆日志
        UserLoginRecord userLoginRecord = new UserLoginRecord();
        userLoginRecord.setUserId(userInfo.getId());
        userLoginRecord.setIp(IpUtils.getIpAddressAtService(request));
        userLoginRecord.setLoginType(loginType);
        userLoginRecordService.save(userLoginRecord);
    }

}
